package com.smedi.ismedi.constructionproject.core.application.service.impl;

import com.smedi.ismedi.constructionproject.core.application.command.ProjectAssignmentCommand;
import com.smedi.ismedi.constructionproject.core.application.command.ProjectAssignmentEditCommand;
import com.smedi.ismedi.constructionproject.core.application.service.ProjectAssignmentApplicationService;
import com.smedi.ismedi.constructionproject.core.domain.aggregatemodel.ProjectAssignment;
import com.smedi.ismedi.constructionproject.core.domain.repository.ProjectAssignmentRepository;
import com.smedi.ismedi.constructionproject.core.domain.service.ProjectAssignmentDomainService;
import com.smedi.ismedi.constructionproject.core.domain.service.ProjectBasicDomainService;
import com.smedi.ismedi.constructionproject.core.infrastructure.constant.CustomError;
import com.smedi.ismedi.constructionproject.core.port.adapter.restful.convert.AdapterConverter;
import com.smedi.ismedi.constructionproject.core.port.adapter.service.OrganizationRemoteApiService;
import com.smedi.ismedi.constructionproject.core.port.adapter.service.PersonnelRemoteApiService;
import com.smedi.ismedi.web.starter.exception.LogicException;
import com.smedi.ismedi.web.starter.util.DozerUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @Author: xulinjun
 * @Date: 2021-6-24 15:33
 */
@Slf4j
@Service
@AllArgsConstructor
public class ProjectAssignmentApplicationServiceImpl implements ProjectAssignmentApplicationService {
    private final DozerUtil dozerUtil;

    private final ProjectAssignmentDomainService projectAssignmentDomainService;

    private final PersonnelRemoteApiService personnelRemoteApiService;

    private final OrganizationRemoteApiService organizationRemoteApiService;

    private final ProjectBasicDomainService projectBasicDomainService;

    private final ProjectAssignmentRepository projectAssignmentRepository;

    private final AdapterConverter adapterConverter;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ProjectAssignment saveProjectAssignment(ProjectAssignmentCommand projectAssignmentCommand) {
        ProjectAssignment projectAssignment = dozerUtil.convertor(projectAssignmentCommand, ProjectAssignment.class);
        return projectAssignmentDomainService.saveAssignment(projectAssignment);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void postBatchAddExistingPerson(List<Long> personIds, Long projectId, Long projectOrganizationId) {
        // 每次批量添加的人员最多允许20个人
        if (personIds.size() > 20) {
            throw LogicException.raise(CustomError.BATCH_SAVE_MAX_ERROR);
        }

        // 判断项目是否有效
        if (!projectBasicDomainService.validateProject(projectId)) {
            throw LogicException.raise(CustomError.PROJECT_NOT_AVAILABLE);
        }

        // 判断组织有效性
        boolean externalUnit;
        if (!organizationRemoteApiService.checkProjectOrganizationAvailable(projectOrganizationId)) {
            throw LogicException.raise(CustomError.ORG_NOT_AVAILABLE);
        } else {
            externalUnit = organizationRemoteApiService.checkProjectOrganizationIsExternalUnit(projectOrganizationId);
        }

        // 除去重复人员
        Set<Long> set = new HashSet<>(personIds);
        // 判断人员是否有效
        for (Long personId : set) {
            if (!personnelRemoteApiService.checkPersonIsAvailable(personId)) {
                throw LogicException.raise(CustomError.PERSON_NOT_AVAILABLE);
            } else {
                // 不允许在外部单位节点下添加企业内部人员
                if (externalUnit) {
                    if (personnelRemoteApiService.checkPersonIsInternal(personId)) {
                        throw LogicException.raise(CustomError.EXTERNAL_ORG_NOT_ALLOW_INTERNAL_PERSON_ASSIGNMENT);
                    }
                }
                // 判断当前人员是否有分配信息
                if (projectAssignmentDomainService.duplicateAssignment(personId, projectId, projectOrganizationId)) {
                    throw LogicException.raise(CustomError.DUPLICATE_PROJECT_ASSIGNMENT);
                }
            }
        }

        // 批量导入数据
        for (Long personId : set) {
            projectAssignmentDomainService.saveAssignment(new ProjectAssignment()
                    .setProjectId(projectId)
                    .setPersonId(personId)
                    .setProjectOrganizationId(projectOrganizationId));
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteProjectAssignment(Long projectAssignmentId) {
        projectAssignmentDomainService.deleteAssignment(projectAssignmentId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteProjectAssignmentsByPersonIdAndProjectId(Long personId,Long projectId) {
        projectAssignmentDomainService.doDeleteByPersonIdAndProjectId(personId,projectId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteProjectAssignmentsByPersonId(Long personId) {
        projectAssignmentDomainService.doDeleteByPersonId(personId);
    }

    @Override
    public Boolean validateProjectOrganizationHasAssignment(Long projectOrganizationId) {
        return projectAssignmentDomainService.validateProjectOrganizationHasAssignment(projectOrganizationId);
    }

    @Override
    public List<Long> queryPersonIdsByCurrentProjectOrganizationId(Long projectOrganizationId) {
        return projectAssignmentRepository.selectProjectAssignmentByProjectOrganizationId(projectOrganizationId).stream()
                .map(ProjectAssignment::getPersonId).collect(Collectors.toList());
    }

    @Override
    public void editProjectAssignment(ProjectAssignmentEditCommand projectAssignmentEditCommand) {
        ProjectAssignment projectAssignment = adapterConverter.projectAssignmentEditCommandToProjectAssignment(projectAssignmentEditCommand);
        projectAssignmentDomainService.editProjectAssignment(projectAssignment);
    }
}
